package com.skivingcloud.file.service.impl;

import com.mybatisflex.core.activerecord.MapperModel;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryColumn;
import com.mybatisflex.core.query.QueryMethods;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.skivingcloud.admin.security.SecurityUser;
import com.skivingcloud.common.utils.PageParameter;
import com.skivingcloud.common.utils.UuidUtil;
import com.skivingcloud.file.entity.SysFile;
import com.skivingcloud.file.mapper.SysFileMapper;
import com.skivingcloud.file.service.SysFileService;
import com.skivingcloud.file.store.FileStorageDao;
import com.skivingcloud.file.utils.Constant;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 附件 服务层实现。
 *
 * @author skivingcloud
 * @since 2024-01-22
 */
@Service
public class SysFileServiceImpl extends ServiceImpl<SysFileMapper, SysFile> implements SysFileService {
    
    /**
     * 文件存储的实现类集合，会根据fileStore来决定最终使用哪个实现类，所以实现类bean命名必须遵循fileStore+"FileStorageDao"的原则(bean名称，非类名)
     */
    @Autowired
    private Map<String, FileStorageDao> fileStorageDaoMap;
    
    /**
     * 文件存储方案，fs:本地文件系统、hdfs:hdfs存储、filenet:filenet存储，其他方案请自行扩展。配置在application.properties中
     */
    @Value("${skivingcloud.file.store:fs}")
    private String fileStore;
    private MapperModel<Object> baseMapper;
    
    /**
     * 分页查询附件
     *
     * @param parameter 查询参数
     * @return 分页查询结果
     */
    @Override
    public Page<SysFile> queryByPage(PageParameter<SysFile> parameter){
        Page<SysFile> page = new Page<>(parameter.getPage(), parameter.getPageSize());
        QueryWrapper queryWrapper = QueryWrapper.create();
        return this.page(page, queryWrapper);
    }
    
    /**
     * 上传添加文件
     *
     * @param files      文件
     * @param businessId 业务id
     * @param user       用户
     * @param belongModule 模块
     * @return 文件id
     */
    @Override
    public List<String> saveFiles(List<MultipartFile> files, String businessId, SecurityUser user, String belongModule) throws Exception{
        List<String> ids = new ArrayList<>();
        LocalDateTime date = LocalDateTime.now();
        for (MultipartFile file : files) {
            // 文件名
            String fileName = file.getOriginalFilename();
            // 后缀
            String extension = FilenameUtils.getExtension(fileName);
            // 字节数
            long fileSize = file.getSize();
            InputStream fileInputStream = file.getInputStream();
            SysFile fileManagement = new SysFile();
            fileManagement.setId(UuidUtil.get32UUID());
            fileManagement.setFileName(fileName);
            fileManagement.setFileType(extension);
            fileManagement.setFileSize(((float) fileSize) / 1024);// 转成KB
            fileManagement.setFileState(Constant.SysFileState.NORMAL.getValue());
            fileManagement.setBusinessId(businessId);
            fileManagement.setBelongModule(belongModule);
            fileManagement.setStoreType(fileStore);
            fileManagement.setVersion(0);
            fileManagement.setCreateUser(user.getId());
            fileManagement.setCreateTime(date);
            FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + Constant.FILE_DAO_END_TEXT);
            fileStorageDao.saveFile(fileManagement, fileInputStream);
            fileManagement.setFilePath("");
            this.mapper.insert(fileManagement);
            ids.add(fileManagement.getId());
        }
        return ids;
    }
    
    /**
     * 下载文件
     *
     * @param fileManagement 文件
     * @param response       response
     */
    @Override
    public void downloadFile(SysFile fileManagement, HttpServletResponse response) throws Exception{
        response.setContentType("application/octet-stream;charset=UTF-8");
        response.setHeader("Content-Disposition",
                "attachment;filename=" + java.net.URLEncoder.encode(fileManagement.getFileName(), StandardCharsets.UTF_8));//
        response.addHeader("Pargam", "no-cache");
        response.addHeader("Cache-Control", "no-cache");
        OutputStream os = response.getOutputStream();
        FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + Constant.FILE_DAO_END_TEXT);
        InputStream fileInputStream = fileStorageDao.getFile(fileManagement);
        byte[] buffer = new byte[1024];
        int hasRead;
        while ((hasRead = fileInputStream.read(buffer)) != -1){
            os.write(buffer,0,hasRead);
        }
    }
    
    /**
     * 返回文件字节
     *
     * @param fileManagement 文件对象
     * @return 字节流
     * @throws Exception 异常
     */
    @Override
    public byte[] getByte(SysFile fileManagement) throws Exception{
        FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + Constant.FILE_DAO_END_TEXT);
        InputStream fileInputStream = fileStorageDao.getFile(fileManagement);
        return fileInputStream.readAllBytes();
    }
    
    /**
     * 将文件移动至“已删除”目录
     *
     * @param asList    文件id
     * @param userId    用户
     * @param fileState 状态
     * @throws Exception 异常
     */
    @Override
    public void moveFilePath(List<String> asList, String userId, String fileState) throws Exception{
        QueryWrapper queryWrapper = QueryWrapper.create().and(SysFile::getId).in(asList);
        List<SysFile> fileManagementList = this.list(queryWrapper);
        for(SysFile f : fileManagementList){
            if(!"3".equals(f.getFileState())){
                f.setFileState(fileState);// 把文件移到待删除文件夹,
                f.setModTime(LocalDateTime.now());
                f.setModUser(userId);
                FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + Constant.FILE_DAO_END_TEXT);
                fileStorageDao.moveFilePath(f, fileState);
                this.mapper.update(f);
            }
        }
    }
    
    /**
     * 根据业务id查询附件信息
     *
     * @param businessId 业务id
     * @return 文件列表
     */
    @Override
    public List<SysFile> getFileListByBusId(String businessId){
        QueryWrapper queryWrapper = QueryWrapper.create().and(SysFile::getBusinessId).eq(businessId)
                .and(SysFile::getFileState).eq(Constant.SysFileState.NORMAL.getValue());
        return this.list(queryWrapper);
    }
    
    /**
     * 根据业务id查询附件数量
     *
     * @param busIds 业务id数组
     * @return 数量
     */
    @Override
    public List<Map> getFileCountByBusId(String[] busIds){
        QueryWrapper queryWrapper = QueryWrapper.create().select(new QueryColumn("business_id"), QueryMethods.count(SysFile::getId).as("fileCount"))
                .where(SysFile::getBusinessId).in(busIds, true)
                .and(SysFile::getFileState).eq(Constant.SysFileState.NORMAL.getValue())
                .groupBy(SysFile::getBusinessId);
        return this.baseMapper.baseMapper().selectListByQueryAs(queryWrapper, Map.class);
    }

    @Override
    public String saveFileInputStream(InputStream inputStream, String businessId, String userId, String belongModule, String fileName) throws Exception {
        // 后缀
        String extension = FilenameUtils.getExtension(fileName);
        // 字节数
        long fileSize = inputStream.available();
        SysFile fileManagement = new SysFile();
        fileManagement.setId(UuidUtil.get32UUID());
        fileManagement.setFileName(fileName);
        fileManagement.setFileType(extension);
        fileManagement.setFileSize(((float) fileSize) / 1024);// 转成KB
        fileManagement.setFileState(Constant.SysFileState.NORMAL.getValue());
        fileManagement.setBusinessId(businessId);
        fileManagement.setBelongModule(belongModule);
        fileManagement.setStoreType(fileStore);
        fileManagement.setVersion(0);
        fileManagement.setCreateUser(userId);
        fileManagement.setCreateTime(LocalDateTime.now());
        FileStorageDao fileStorageDao = fileStorageDaoMap.get(fileStore + Constant.FILE_DAO_END_TEXT);
        fileStorageDao.saveFile(fileManagement, inputStream);
        fileManagement.setFilePath("");
        this.mapper.insert(fileManagement);
        return fileManagement.getId();
    }
}
